/**
 * 
 */
package fr.cephb.joperon.webapp;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamWriter;

import fr.cephb.joperon.core.Fid;
import fr.cephb.joperon.core.Operon;
import fr.cephb.joperon.core.bio.Assembly;
import fr.cephb.joperon.core.bio.Chromosome;
import fr.cephb.joperon.core.db.GBuildDB;
import fr.cephb.joperon.core.db.SnpMapDB;
import fr.cephb.joperon.core.entities.SnpMap;
import fr.cephb.joperon.tool.OperonFactory;
import fr.cephb.util.Cast;

/**
 * BIODAS Server for OPeron
 * 
 *
 */
public class DASServer extends HttpServlet
	{
	private static class Segment
		{
		Chromosome chrom;
		long start;
		long end;
		}
	/**
	 * DASServer
	 */
	private static final long serialVersionUID = 1L;
@Override
protected void service(HttpServletRequest req, HttpServletResponse res)
		throws ServletException, IOException {
	String pathInfo= req.getPathInfo();
	if(pathInfo==null) throw new ServletException("Path is null");
	String requestURL= req.getRequestURL().toString();
	Operon operon=null;
	OperonFactory factory=null;
	/* Retrieves the list of data sources available from this server  */
	try
		{
		if(pathInfo.equals("/dsn"))
			{
			OutputStream out= res.getOutputStream();
	    	XMLOutputFactory xmlfactory= XMLOutputFactory.newInstance();
	    	XMLStreamWriter w= xmlfactory.createXMLStreamWriter(out);
			res.setContentType( "text/xml");
			
			w.writeStartDocument();
			w.writeDTD("<!DOCTYPE DASDSN SYSTEM \'http://www.biodas.org/dtd/dasdsn.dtd\'>");
			w.writeStartElement("DASDSN");
			
			for(Assembly as: Assembly.getAssemblies())
				{
				w.writeStartElement("DSN");
				
				w.writeStartElement("SOURCE");
				w.writeAttribute("id", String.valueOf(as.getId()));
				w.writeAttribute("version", "1.0");
				w.writeCharacters(as.getName());
				w.writeEndElement();
				
				w.writeStartElement("MAPMASTER");
				w.writeCharacters(requestURL.substring(0,requestURL.length()-4)+"/"+as.getId());
				w.writeEndElement();
				
				w.writeStartElement("DESCRIPTION");
				w.writeCharacters(as.getName());
				w.writeEndElement();
				
				w.writeEndElement();
				}
			
			w.writeEndElement();
			w.writeEndDocument();
			w.flush();
			w.close();
			}
		else if(pathInfo.matches("/[0-9]+/dna"))
			{
			int j= pathInfo.indexOf('/',1);
			if(j==-1) throw new ServletException("Bad path");
			String as= pathInfo.substring(1,j);
			if(!Cast.Integer.isA(as))throw new ServletException("Bad assembly");
			Assembly assembly= Assembly.getAssemblyById(Integer.parseInt(as));
			if(assembly==null) throw new ServletException("Bad assembly");
			
			operon= Operon.newInstance();
			factory= new OperonFactory(operon);
			
			
			List<Segment> segments= getSegments(req);
			if(segments==null || segments.isEmpty()) throw new ServletException("One segment required");
			
			
			OutputStream out= res.getOutputStream();
	    	XMLOutputFactory xmlfactory= XMLOutputFactory.newInstance();
	    	XMLStreamWriter w= xmlfactory.createXMLStreamWriter(out);
	    	res.setContentType( "text/xml");
			
			w.writeStartDocument();
			w.writeDTD("<!DOCTYPE DASDNA SYSTEM \"http://www.biodas.org/dtd/dasdna.dtd\">");
			w.writeStartElement("DASDNA");
			w.writeComment("NO IMPLEMENTED");
			
			
			
			for(Segment seg:segments)
				{
				w.writeStartElement("SEQUENCE");
				w.writeAttribute("id",seg.chrom.toString());
				w.writeAttribute("start",String.valueOf(seg.start));
				w.writeAttribute("stop",String.valueOf(seg.end));
				w.writeAttribute("version","1.0");
				
				w.writeStartElement("DNA");
				w.writeAttribute("length",String.valueOf((seg.end-seg.start)+1));
				
				final int buff=100000;
				StringBuilder b= new StringBuilder(100000);
				while(true)
					{
					while(seg.start<=seg.end && b.length()< buff)
						{
						b.append("N");
						seg.start++;
						}
					if(b.length()==0) break;
					w.writeCharacters(b.toString());//TODO
					w.writeCharacters("\n");
					b.setLength(0);
					}
					
				
				w.writeEndElement();
				
				w.writeEndElement();
				}
			
			
			
			w.writeEndElement();
			w.writeEndDocument();
			w.flush();
			w.close();
			}
		else if(pathInfo.matches("/[0-9]+/types"))
			{
			int j= pathInfo.indexOf('/',1);
			if(j==-1) throw new ServletException("Bad path");
			String as= pathInfo.substring(1,j);
			if(!Cast.Integer.isA(as))throw new ServletException("Bad assembly");
			Assembly assembly= Assembly.getAssemblyById(Integer.parseInt(as));
			if(assembly==null) throw new ServletException("Bad assembly");
			
			operon= Operon.newInstance();
			factory= new OperonFactory(operon);
			
			
			List<Segment> segments= getSegments(req);
			
			
			
			OutputStream out= res.getOutputStream();
	    	XMLOutputFactory xmlfactory= XMLOutputFactory.newInstance();
	    	XMLStreamWriter w= xmlfactory.createXMLStreamWriter(out);
	    	res.setContentType( "text/xml");
			
			w.writeStartDocument();
			w.writeDTD("<!DOCTYPE DASTYPES SYSTEM \"http://www.biodas.org/dtd/dastypes.dtd\">");
			w.writeStartElement("DASTYPES");
			w.writeStartElement("GFF");
			w.writeAttribute("href",requestURL+(req.getQueryString()==null?"":"?"+req.getQueryString()));
			w.writeAttribute("version","1.0");
			
			if(segments!=null && !segments.isEmpty())
				{
				for(Segment seg:segments)
					{
					w.writeStartElement("SEGMENT");
					w.writeAttribute("id",seg.chrom.toString());
					w.writeAttribute("start",String.valueOf(seg.start));
					w.writeAttribute("stop",String.valueOf(seg.end));
					w.writeAttribute("version","1.0");
					
					w.writeEmptyElement("TYPE");
					w.writeAttribute("id","snpMap");
					w.writeAttribute("category","other");
					
					w.writeEndElement();
					}
				}
			else
				{
				w.writeStartElement("SEGMENT");
				w.writeAttribute("version","1.0");
				w.writeAttribute("label","Types");
				w.writeEmptyElement("TYPE");
				w.writeAttribute("id","snpMap");
				w.writeAttribute("method","");
				w.writeAttribute("category","other");
				w.writeEndElement();
				}
			
			
			w.writeEndElement();
			w.writeEndDocument();
			w.flush();
			w.close();
			}
		
		else if(pathInfo.matches("/[0-9]+/entry_points"))
			{
			int j= pathInfo.indexOf('/',1);
			if(j==-1) throw new ServletException("Bad path");
			String as= pathInfo.substring(1,j);
			if(!Cast.Integer.isA(as))throw new ServletException("Bad assembly");
			Assembly assembly= Assembly.getAssemblyById(Integer.parseInt(as));
			if(assembly==null) throw new ServletException("Bad assembly");
			
			operon= Operon.newInstance();
			factory= new OperonFactory(operon);
			List<GBuildDB.ChromInfo> abouChroms= new ArrayList<GBuildDB.ChromInfo>();
			if(factory.isGBuildExists(assembly))
				{
				abouChroms = factory.getGBuild(assembly).getChromosomes();
				}
			
			OutputStream out= res.getOutputStream();
	    	XMLOutputFactory xmlfactory= XMLOutputFactory.newInstance();
	    	XMLStreamWriter w= xmlfactory.createXMLStreamWriter(out);
	    	res.setContentType( "text/xml");
			
			w.writeStartDocument();
			w.writeDTD("<!DOCTYPE DASEP SYSTEM \'http://www.biodas.org/dtd/dasep.dtd\'>");
			w.writeStartElement("DASEP");
			w.writeStartElement("ENTRY_POINTS");
			w.writeAttribute("href",requestURL);
			w.writeAttribute("version","1.0");
			
			
			for(GBuildDB.ChromInfo ci: abouChroms)
				{

				w.writeStartElement("SEGMENT");
				w.writeAttribute("id",ci.getChromosome().toString());
				w.writeAttribute("start",String.valueOf(ci.getStart()));
				w.writeAttribute("stop",String.valueOf(ci.getEnd()));
				w.writeAttribute("orientation","+");
				w.writeAttribute("subparts","no");
				w.writeCharacters(ci.getChromosome().toString());
				w.writeEndElement();
				}
			
			w.writeEndElement();
			w.writeEndElement();
			w.writeEndDocument();
			w.flush();
			w.close();
			}
		else if(pathInfo.matches("/[0-9]+/features"))
			{
			int j= pathInfo.indexOf('/',1);
			if(j==-1) throw new ServletException("Bad path");
			String as= pathInfo.substring(1,j);
			if(!Cast.Integer.isA(as))throw new ServletException("Bad assembly");
			Assembly assembly= Assembly.getAssemblyById(Integer.parseInt(as));
			if(assembly==null) throw new ServletException("Bad assembly");
			
			operon= Operon.newInstance();
			factory= new OperonFactory(operon);
			
			
			List<Segment> segments= getSegments(req);
			if(segments==null) segments= new ArrayList<Segment>();
			String types[]= req.getParameterValues("type");
			if(types==null) types=new String[0];
			Set<String> typeset= new HashSet<String>(Arrays.asList(types));
			
			OutputStream out= res.getOutputStream();
	    	XMLOutputFactory xmlfactory= XMLOutputFactory.newInstance();
	    	XMLStreamWriter w= xmlfactory.createXMLStreamWriter(out);
	    	res.setContentType( "text/xml");
			
			w.writeStartDocument();
			w.writeDTD("<!DOCTYPE DASGFF SYSTEM \"http://www.biodas.org/dtd/dasgff.dtd\">");
			w.writeStartElement("DASGFF");
			w.writeStartElement("GFF");
			w.writeAttribute("href",requestURL+(req.getQueryString()==null?"":"?"+req.getQueryString()));
			w.writeAttribute("version","1.0");
			
			
			for(Segment seg:segments)
				{
				w.writeStartElement("SEGMENT");
				w.writeAttribute("id",seg.chrom.toString());
				w.writeAttribute("label",seg.chrom.toString());
				w.writeAttribute("start",String.valueOf(seg.start));
				w.writeAttribute("stop",String.valueOf(seg.end));
				w.writeAttribute("version","1.0");
				
				if((typeset.isEmpty() || typeset.contains("snpMap")) &&
					factory.isSnpMapExists(assembly))
					{
					SnpMapDB.ValueIterator iter=null;
					try
						{
						iter=factory.getSnpMap(assembly).listBetween(seg.chrom,
					
							(int)Math.min(seg.start,Fid.MAX_VALUE),
							(int)Math.min(seg.end,Fid.MAX_VALUE)
							);
						while(iter.hasNext())
							{
							SnpMap snp=iter.next();
							w.writeStartElement("FEATURE");
							w.writeAttribute("id",snp.getAcn());
							w.writeAttribute("label",snp.getAcn());
							
							w.writeStartElement("TYPE");
							w.writeAttribute("id","snpMap");
							w.writeAttribute("category","snpMap");
							w.writeAttribute("reference","no");
							w.writeCharacters("snpMap");
							w.writeEndElement();
							
							
							w.writeStartElement("METHOD");
							w.writeEndElement();
							
							w.writeStartElement("START");
							w.writeCharacters(String.valueOf(snp.getPosition()));
							w.writeEndElement();
							
							w.writeStartElement("END");
							w.writeCharacters(String.valueOf(snp.getPosition()));
							w.writeEndElement();
							
							w.writeStartElement("SCORE");
							w.writeCharacters("-");
							w.writeEndElement();
							
							w.writeStartElement("ORIENTATION");
							w.writeCharacters("0");
							w.writeEndElement();
							
							
							w.writeStartElement("PHASE");
							w.writeCharacters("-");
							w.writeEndElement();
							
							
							w.writeEndElement();
							}
						}
					catch(Exception err)
						{
						throw new ServletException(err);
						}
					finally
						{
						if(iter!=null) iter.close();
						}
					
					
					
					}
				
				w.writeEndElement();
				}
			
			w.writeEndElement();
			w.writeEndElement();
			w.writeEndDocument();
			w.flush();
			w.close();
			}
		else
			{
			throw new ServletException("Illegal Path");
			}
		} 
	catch(ServletException err)
		{
		throw err;
		}
	catch(IOException err)
		{
		throw err;
		}
	catch(Throwable err)
		{
		throw new ServletException(err);
		}
	finally
		{
		if(factory!=null) factory.close();
		if(operon!=null) operon.safeClose();
		}
	}

private List<Segment> getSegments(HttpServletRequest req) throws ServletException
	{
	String array[]=req.getParameterValues("segment");
	if(array==null) return null;
	List<Segment> list= new ArrayList<Segment>();
	for(String s:array)
		{
		Segment seg= new Segment();
		int i=s.indexOf(':');
		if(i<1)
			{
			String s2= s;
			if(!Cast.Integer.isA(s2))  throw new ServletException("Illegal segment"+s);
			seg.chrom= Chromosome.getById(Cast.Integer.cast(s2));
			if(seg.chrom==null || seg.chrom==Chromosome.ChrUndefined)  throw new ServletException("Illegal segment"+s);
			seg.start=0L;
			seg.end=Fid.MAX_VALUE;
			list.add(seg);
			continue;
			}
		
		
		if(i<1 || i+1==s.length()) throw new ServletException("Illegal segment"+s);
		int j= s.indexOf(',',i);
		if(j==-1 || j+1==s.length()) throw new ServletException("Illegal segment"+s);
		String s2= s.substring(0,i);
		if(!Cast.Integer.isA(s2))  throw new ServletException("Illegal segment"+s);
		seg.chrom= Chromosome.getById(Cast.Integer.cast(s2));
		if(seg.chrom==null || seg.chrom==Chromosome.ChrUndefined)  throw new ServletException("Illegal segment"+s);
		 s2= s.substring(i+1,j);
		if(!Cast.Long.isA(s2))  throw new ServletException("Illegal segment"+s);
		seg.start= Cast.Long.cast(s2);
		if(seg.start<0 || seg.start==Long.MAX_VALUE)  throw new ServletException("Illegal segment"+s);
		s2= s.substring(j+1);
		if(!Cast.Long.isA(s2))  throw new ServletException("Illegal segment"+s);
		seg.end= Cast.Long.cast(s2);
		if(seg.end<0 || seg.end==Long.MAX_VALUE)  throw new ServletException("Illegal segment"+s);
		list.add(seg);
		}
	return list;
	}


}
